package com.uobgroup.ews.uam.core.domain;

import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.springframework.beans.factory.annotation.Configurable;

@Configurable
@Entity
@NamedQueries({
      @NamedQuery(name = "Segment.findAllReferencesThatHasNoSegment", query = "SELECT R FROM Reference R WHERE R.referenceType = :referenceType AND R.id NOT IN (SELECT SGM.segmentReference.id FROM Segment SGM)"),
      @NamedQuery(name = "Reference.findReferencesByReferenceTypeScope", query = "SELECT R FROM Reference R WHERE R.referenceType.scope = :scope"),
      @NamedQuery(name = "Reference.countReferencesByReferenceTypeScope", query = "SELECT COUNT(R) FROM Reference R WHERE R.referenceType.scope = :scope"),
      @NamedQuery(name = "Reference.countReferencesByType", query = "SELECT COUNT(R) FROM Reference R WHERE R.referenceType = :referenceType"),
      @NamedQuery(name = "Reference.findReferenceTypesThatHasReferencesByScope", query = "SELECT DISTINCT R.referenceType FROM Reference R WHERE R.referenceType.scope = :scope"),
      @NamedQuery(name = "Reference.findReferencesByType", query = "SELECT R FROM Reference R WHERE R.referenceType = :referenceType")})
public class Reference extends BaseEntity {
   
   public static final String QUERY_FIND_REFERENCE_THAT_HAS_REFERENCES_BY_SCOPE = "Reference.findReferenceTypesThatHasReferencesByScope";
   
   public static final String QUERY_COUNT_REFERENCE_BY_TYPE = "Reference.countReferencesByType";
   
   public static final String QUERY_FIND_REFERENCE_BY_TYPE = "Reference.findReferencesByType";

   public static final String PARAM_REFERENCE_TYPE = "referenceType";
   
   public static final String PARAM_SCOPE = "scope";

   public static final String QUERY_FIND_REFERENCES_THAT_HAS_NO_SEGMENT = "Segment.findAllReferencesThatHasNoSegment";
   
   public static final String QUERY_FIND_REFERENCES_BY_SCOPE = "Reference.findReferencesByScope";
   
   public static final String QUERY_FIND_REFERENCES_BY_REFERENCE_TYPE_SCOPE = "Reference.findReferencesByReferenceTypeScope";

   public static final String QUERY_COUNT_REFERENCE_BY_REFERENCE_TYPE_SCOPE = "Reference.countReferencesByReferenceTypeScope";
   
   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   @Column(name = "id")
   private Long id;
   
   @NotNull
   @Size(max = 255)
   private String name;

   @ManyToOne
   private ReferenceType referenceType;

   @NotNull
   @Size(max = 64)
   private String code;

   @OneToOne(mappedBy = "segmentReference", cascade = CascadeType.REMOVE)
   private Segment segment;

   @ManyToMany(mappedBy = "segments", cascade = { CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH })
   private List<Profile> profiles;
   
   @Transient
   private String segmentSecurityDisplayName;

   public String getSegmentSecurityDisplayName() {
      if (segment != null && segment.getSecurityGroup() != null) {
         return segment.getSecurityGroup().getDisplayName();
      }
      return segmentSecurityDisplayName;
   }

   public void setSegmentSecurityDisplayName(String segmentSecurityDisplayName) {
      this.segmentSecurityDisplayName = segmentSecurityDisplayName;
   }

   public String getName() {
      return this.name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public String getCode() {
      return this.code;
   }

   public void setCode(String code) {
      this.code = code;
   }

   public Long getId() {
      return this.id;
   }

   public void setId(Long id) {
      this.id = id;
   }

   public Segment getSegment() {
      return segment;
   }

   public void setSegment(Segment segment) {
      this.segment = segment;
   }

   public List<Profile> getProfiles() {
      return profiles;
   }

   public void setProfiles(List<Profile> profiles) {
      this.profiles = profiles;
   }

   @Transient
   private Object entity;

   @Transient
   private Integer hashcodeValue = null;
   
   public Object getEntity() {
      return entity;
   }

   public void setEntity(Object entity) {
      this.entity = entity;
   }
   
   public boolean equals(Object other) {
      if (!(other instanceof Reference)) {
         return false;
      }
      Reference castOther = (Reference) other;
      return new EqualsBuilder().append(this.getId(), castOther.getId()).isEquals();
   }
   
   public int hashCode() {
      if (hashcodeValue == null) {
         if (id == null) {
            hashcodeValue = super.hashCode();
         }
         else {
            hashcodeValue = generateHashCode();
         }
      }
      return hashcodeValue;
   }

   public int generateHashCode() {
      return new HashCodeBuilder().append(getId()).toHashCode();
   }
   
   public ReferenceType getReferenceType() {
      return referenceType;
   }

   public void setReferenceType(ReferenceType referenceType) {
      this.referenceType = referenceType;
   }

}
